package cc.shacocloud.mirage.bean.impl;

import cc.shacocloud.mirage.bean.BeanKeyMap;
import cc.shacocloud.mirage.bean.exception.BeanException;
import cc.shacocloud.mirage.bean.exception.NoSuchBeanException;
import cc.shacocloud.mirage.bean.meta.BeanKey;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.annotation.Annotation;
import java.util.*;
import java.util.function.BiPredicate;
import java.util.stream.Collectors;

/**
 * {@link BeanKeyMap} 的默认实现，子类可以继续拓展
 *
 * @author 思追(shaco)
 */
public abstract class AbstractBeanKeyMap<S> implements BeanKeyMap<S> {
    
    protected final Map<BeanKey, S> map = new HashMap<>(128);
    
    protected final Logger log = LoggerFactory.getLogger(getClass());
    
    /**
     * 添加
     */
    @Override
    public synchronized void add(@NotNull BeanKey beanKey, @NotNull S mappingData) {
        if (log.isDebugEnabled() && containsKey(beanKey)) {
            S old = map.get(beanKey);
            
            if (old.equals(mappingData)) {
                return;
            }
            
            log.debug("更新 {} 的映射信息，旧的映射[{}]将被[{}]覆盖！", beanKey, old, mappingData);
        }
        
        map.put(beanKey, mappingData);
    }
    
    @Nullable
    public synchronized S find(@NotNull BeanKey beanKey, boolean throwEx) throws BeanException {
        LinkedList<BeanKey> matches = new LinkedList<>();
        
        for (BeanKey bean : map.keySet()) {
            if (canInject(beanKey, bean)) {
                matches.add(bean);
            }
        }
        
        if (matches.size() == 0) {
            if (throwEx) {
                throw new NoSuchBeanException("没有这样的Bean：" + beanKey);
            } else {
                return null;
            }
        } else if (matches.size() > 1) {
            throw new BeanException("找到多个满足 " + beanKey + " 的Bean：" + matches);
        }
        
        return map.get(matches.peekFirst());
    }
    
    @Override
    public @Nullable S get(@NotNull BeanKey beanKey) {
        return map.get(beanKey);
    }
    
    @Override
    public @NotNull Collection<BeanKey> findBeanKeyBy(BiPredicate<BeanKey, S> predicate) {
        return map.entrySet().stream()
                .filter(entry -> predicate.test(entry.getKey(), entry.getValue()))
                .map(Map.Entry::getKey)
                .collect(Collectors.toList());
    }
    
    @NotNull
    @Override
    public Map<BeanKey, S> getAll() {
        return new HashMap<>(map);
    }
    
    /**
     * 返回 Bean 数据映射的大小
     */
    @Override
    public synchronized int size() {
        return map.size();
    }
    
    /**
     * 检查映射中是否存在 Bean 数据。
     */
    @Override
    public synchronized boolean contains(@NotNull BeanKey beanKey) {
        for (BeanKey bean : map.keySet()) {
            if (canInject(beanKey, bean)) {
                return true;
            }
        }
        return false;
    }
    
    @Override
    public boolean containsKey(@NotNull BeanKey beanKey) {
        return map.containsKey(beanKey);
    }
    
    /**
     * 搜索与描述匹配的所有 bean，并返回{@link BeanKey}
     * <p>
     * 这些键可用于获取实际的 Bean 数据。
     */
    @NotNull
    @Override
    public synchronized Collection<BeanKey> findBeanKeys(@NotNull BeanKey beanKey) {
        Collection<BeanKey> matches = new ArrayList<>();
        for (BeanKey bean : map.keySet()) {
            if (canInject(beanKey, bean)) {
                matches.add(bean);
            }
        }
        
        return matches;
    }
    
    /**
     * 基于注解查询匹配的所有 bean，并返回 {@link BeanKey}
     * <p>
     * 这些键可用于获取实际的 Bean 数据
     */
    @NotNull
    @Override
    public synchronized Collection<BeanKey> findBeanKeysByAnnotation(@NotNull Class<? extends Annotation> annotation) {
        Collection<BeanKey> matches = new ArrayList<>();
        for (BeanKey bean : map.keySet()) {
            if (bean.hasAnnotation(annotation)) {
                matches.add(bean);
            }
        }
        
        return matches;
    }
    
    @Override
    public synchronized void remove(@NotNull BeanKey beanKey) {
        Iterator<Map.Entry<BeanKey, S>> iterator = map.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<BeanKey, S> entry = iterator.next();
            BeanKey bean = entry.getKey();
            if (canInject(beanKey, bean)) {
                iterator.remove();
            }
        }
    }
    
    @Override
    public void removeKey(@NotNull BeanKey beanKey) {
        map.remove(beanKey);
    }
    
    /**
     * 判断 目标{@link BeanKey} 能否注入 源{@link BeanKey}
     *
     * @param source 源 {@link BeanKey}
     * @param target 目标 {@link BeanKey}
     * @return 可以注入返回 true 反之为 false
     */
    protected boolean canInject(@NotNull BeanKey source, @NotNull BeanKey target) {
        return source.canInject(target);
    }
    
    
}
